//-------------------------------------------------------------------------------------------------
// <copyright file="StyleFix.cs" company="OfficeClip LLC">
// Copyright (c) OfficeClip LLC.  All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------------------

namespace OfficeClip.OpenSource.StyleFix
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Text;
    using System.Text.RegularExpressions;
    using StyleCop;
    using System.Linq;

    /// <summary>
    /// A class to fix the styles
    /// </summary>
    public class StyleFix
    {
        public static StringBuilder ViolationContent;

        protected string FilePath;

        private bool fileChanged;

        List<SFWorkingLine> sfWorkingLines = new List<SFWorkingLine>();

        public List<SAObject> Violations
        {
            get
            {
                return violations;
            }
        }

        List<SAObject> violations = new List<SAObject>();

        public StyleFix(string projectPath, string filePath)
        {
            fileChanged = false;
            decimal lineNumber = 0;
            SFWorkingLine sfWorkingLine;
            string input;
            if (File.Exists(filePath))
            {
                StreamReader sr = new StreamReader(File.OpenRead(filePath));
                Encoding fileEncoding = sr.CurrentEncoding;
                while ((input = sr.ReadLine()) != null)
                {
                    lineNumber++;
                    sfWorkingLine = new SFWorkingLine(lineNumber, input, false, new List<SAObject>());
                    sfWorkingLines.Add(sfWorkingLine);
                }
                sr.Close();
                if (sfWorkingLines.Count == 0)
                {
                    return;
                }

                RunStyleCop(projectPath, filePath);
                if (Violations.Count > 0)
                {
                    FixStyleCop();
                }
                if (fileChanged)
                {
                    // Write the output file
                    using (StreamWriter sw = new StreamWriter(filePath, false, fileEncoding))
                    {
                        sw.Write(OutputFileContent());
                    }
                }
            }
        }

        /// <summary>
        /// Outputs the content of the file.
        /// </summary>
        /// <returns>The output content</returns>
        public string OutputFileContent()
        {
            StringBuilder sb = new StringBuilder();
            foreach (SFWorkingLine workingLine in sfWorkingLines)
            {
                if (!workingLine.IsRemove)
                {
                    sb.Append(workingLine.Line);
                    sb.Append(System.Environment.NewLine);
                }
            }

            string outputLines = sb.ToString();
            (new Drivers.DocumentationRules()).SA1633_FileMustHaveHeader(Path.GetFileName(FilePath), ref outputLines);
            return outputLines;
        }

        public void CalculateViolations(out int total, out int corrected)
        {
            total = 0;
            corrected = 0;
            foreach (SFWorkingLine workingLine in sfWorkingLines)
            {
                total += workingLine.TotalViolationsCount;
                corrected += workingLine.TotalFixedViolationsCount;
            }
        }

        private void OnViolationEncountered(object sender, ViolationEventArgs e)
        {
            int column = 1;
            if (e.Location != null)
                column = e.Location.StartPoint.IndexOnLine;
            SAObject Violation = new SAObject(e.Violation.Rule.CheckId, e.LineNumber, column, e.Message, false);
            sfWorkingLines[e.LineNumber - 1].AddViolation(Violation);
            violations.Add(Violation);

            ViolationContent.AppendFormat(
                "{0}:{1}:{2}:{3} - {4}{5}",
                e.Violation.Rule.CheckId,
                e.Violation.SourceCode.Name,
                e.LineNumber,
                column,
                e.Message,
                System.Environment.NewLine);
        }

        private void RunStyleCop(string projectPath, string filePath)
        {
            FilePath = filePath;
            Debug.Assert(sfWorkingLines.Count > 0);
            ViolationContent = new StringBuilder();
            StyleCopConsole console = new StyleCopConsole(null, false, null, null, true);
            CodeProject project = new CodeProject(0, projectPath, new Configuration(null));

            if (console.Core.Environment.AddSourceCode(project, FilePath, null))
            {

                console.OutputGenerated += OnOutputGenerated;
                console.ViolationEncountered += OnViolationEncountered;
                console.Start(new[] { project }, true);
                console.ViolationEncountered -= OnViolationEncountered;
                console.OutputGenerated -= OnOutputGenerated;
            }
        }

        private void OnOutputGenerated(object sender, OutputEventArgs e)
        {

        }

        private void FixSpacingRules()
        {
            Drivers.SpacingRules spacingRules = new Drivers.SpacingRules();
            if (Violations.Any(v => v.ErrorId == "SA1000"))
                spacingRules.SA1000_KeywordsMustBeSpacedCorrectly(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1001"))
                spacingRules.SA1001_CommasMustBeSpaceCorrectly(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1002"))
                spacingRules.SA1002_SemicolonsMustBeSpaceCorrectly(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1003"))
                spacingRules.SA1003_SymbolsMustBeSpaceCorrectly(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1004"))
                spacingRules.SA1004_DocumentationLinesMustBeginWithSingleSpace(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1005"))
                spacingRules.SA1005_SingleLineCommentsMustBeginWithSingleSpace(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1009"))
                spacingRules.SA1009_ClosingParenthesisMustBeSpacedCorrectly(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1010"))
                spacingRules.SA1010_OpeningSquareBracketsMustBeSpacedCorrectly(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1012"))
                spacingRules.SA1012_OpeningCurlyBracketsMustBeSpacedCorrectly(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1013"))
                spacingRules.SA1013_ClosingCurlyBracketsMustBeSpacedCorrectly(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1027"))
                spacingRules.SA1027_TabsMustNotBeUsed(ref sfWorkingLines);

            if (spacingRules.RuleFound)
                fileChanged = true;
        }

        private void FixReadabilityRules()
        {
            Drivers.ReadabilityRules readibilityRules = new Drivers.ReadabilityRules();

            if (Violations.Any(v => v.ErrorId == "SA1101"))
                readibilityRules.SA1101_PrefixLocalCallsWithThis(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1115"))
                readibilityRules.SA1115_ParameterMustFollowComma(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1116"))
                readibilityRules.SA1116_SplitParametersMustStartOnLineAfterDeclaration(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1119"))
                readibilityRules.SA1119_StatementMustNotUseUnnecessaryParenthesis(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1120"))
                readibilityRules.SA1120_CommentsMustContainText(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1121"))
                readibilityRules.SA1121_UseBuiltInTypeAlias(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1126"))
                readibilityRules.SA1126_PrefixCallsCorrectly(ref sfWorkingLines);
            if (readibilityRules.RuleFound)
                fileChanged = true;
        }

        private void FixNamingRules()
        {
            //Disable Naming rules since this can cause breaking changes in code
            return;
            //TODO SA1306
            Drivers.NamingRules namingRules = new Drivers.NamingRules();

            if (Violations.Any(v => v.ErrorId == "SA1300"))
                namingRules.SA1300_ElementMustBeginWithUpperCaseLetter(ref sfWorkingLines);

            if (namingRules.RuleFound)
                fileChanged = true;
        }

        private void FixMaintainabilityRules()
        {
            Drivers.MaintainabilityRules layoutRules = new Drivers.MaintainabilityRules();

            if (Violations.Any(v => v.ErrorId == "SA1400"))
                layoutRules.SA1400_AccessModifierMustBeDeclared(ref sfWorkingLines);

            if (layoutRules.RuleFound)
                fileChanged = true;
        }

        private void FixLayoutRules()
        {
            Drivers.LayoutRules layoutRules = new Drivers.LayoutRules();

            if (Violations.Any(v => v.ErrorId == "SA1503"))
                layoutRules.SA1503_CurlyBracketsMustNotBeOmittedTest(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1505"))
                layoutRules.SA1505_AnOpeningCurlyBracketMustNotBeFollowedByABlankLine(ref sfWorkingLines);
            //TODO SA1508
            //TODO SA1512
            if (Violations.Any(v => v.ErrorId == "SA1513"))
                layoutRules.SA1513_ClosingCurlyBracketMustBeFollowedByBlankLine(ref sfWorkingLines);
            //TODO SA1514
            if (Violations.Any(v => v.ErrorId == "SA1515"))
                layoutRules.SA1515_SingleLineCommentMeBePreceededByABlankLine(ref sfWorkingLines);
            if (Violations.Any(v => v.ErrorId == "SA1516"))
                layoutRules.SA1516_ElementsMustBeSeparatedByBlankLine(ref sfWorkingLines);

            if (layoutRules.RuleFound)
                fileChanged = true;
        }

        private void FixStyleCop()
        {
            FixSpacingRules();
            FixReadabilityRules();
            FixNamingRules();
            FixLayoutRules();
            FixMaintainabilityRules();
        }
    }
}